﻿using HY.DataAchieve.Dapper;
using HY.DataAchieve.EntityFramework;
using HY.DataCore;
using HY.DataCore.Migrations;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace HY.DataAchieve
{
    /// <summary>
    /// 工作单元
    /// 说明：当Connection绑定事务时，无论EF的SaveChanges()还是Dapper直接执行SQL语句，都不会保存到数据库，必须提交事务
    /// 说明：为使EF和Dapper在一个事务内，则应保证两者Connection为同一个，即通过相同的DbConnection实例化上下文
    /// 说明：未开启事务时，EF方法需要通过unit.Commit()提交进行保存（封装方法内未写SaveChanges()），Dapper方法直接执行即保存
    /// 说明：开启事务时，均需通过unit.Commit()进行提交
    /// </summary>
    public class UnitOfWork : IDisposable
    {
        public void MigrateDatabaseToLatestVersion()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<EntityContext, Configuration>());
        }

        /// <summary>
        /// EF上下文是否存在
        /// </summary>
        private bool EFContextIsExist { get { return this.efContext != null; } }

        /// <summary>
        /// Dapper上下文是否存在 
        /// </summary>
        private bool DapperContextIsExist { get { return this.dapperContext != null; } }

        /// <summary>
        /// 是否存在事务
        /// </summary>
        private bool HasTransaction { get; set; }

        private EntityContext efContext = null;
        private DapperContext dapperContext = null;

        private Dictionary<Type, object> repositories = new Dictionary<Type, object>();

        /// <summary>
        /// 对不同实体使用统一EF上下文对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEFRepository<T> EF<T>() where T : class
        {
            if (!this.EFContextIsExist)
            {
                //保证DbConnection一致
                if (DapperContextIsExist)
                    efContext = new EntityContext((System.Data.Common.DbConnection)dapperContext.dbConnecttion);
                else
                    efContext = new EntityContext();
                //保证事务一致及关联的DbConnection一致
                if (this.HasTransaction)
                {
                    if (this.DapperContextIsExist)
                    {
                        //如果 dapperContext 存在
                        var trans = this.dapperContext.GetTransaction();
                        if (trans != null)
                        {
                            //并且 dapperContext 的事务存在，就用 dapperContext 的事务作为 efContext 的事务
                            this.efContext.Database.UseTransaction(trans);
                        }
                        else
                        {
                            //否则由 efContext 启动事务，并将该事务设置给 dapperContext
                            this.efContext.Database.BeginTransaction();
                            this.dapperContext.BeginTransaction(this.efContext.Database.CurrentTransaction.UnderlyingTransaction);
                        }
                    }
                    else
                    {
                        //如果 dapperContext 不存在，则由 efContext 启动事务
                        if (this.efContext.Database.CurrentTransaction == null)
                        {
                            this.efContext.Database.BeginTransaction();
                        }
                    }
                }
            }

            if (repositories.Keys.Contains(typeof(T)) == true)
            {
                return repositories[typeof(T)] as IEFRepository<T>;
            }
            IEFRepository<T> repository = new EFRepository<T>(efContext);
            repositories.Add(typeof(T), repository);
            return repository;
        }

        private IDapperHelper dapperHelper = null;

        /// <summary>
        /// Dapper上下文对象
        /// </summary>
        /// <returns></returns>
        public IDapperHelper Dapper()
        {
            if (!this.DapperContextIsExist)
            {
                //保证DbConnection一致
                if (EFContextIsExist)
                    dapperContext = new DapperContext(efContext.Database.Connection);
                else
                    dapperContext = new DapperContext();
                //保证事务一致及关联的DbConnection一致
                if (this.HasTransaction)
                {
                    if (this.EFContextIsExist)
                    {
                        //如果 efContext 存在
                        var trans = this.efContext.Database.CurrentTransaction;
                        if (trans != null)
                        {
                            //并且 efContext 的事务存在，就用 efContext 的事务作为 dapperContext 的事务
                            this.dapperContext.BeginTransaction(trans.UnderlyingTransaction);
                        }
                        else
                        {
                            //否则由 dapperContext 启动事务，并将该事务设置给 efContext
                            this.dapperContext.BeginTransaction();
                            System.Data.Common.DbTransaction tr = this.dapperContext.GetTransaction();
                            this.efContext.Database.UseTransaction(tr);
                        }
                    }
                    else
                    {
                        //如果 efContext 不存在，则由 dapperContext 启动事务
                        if (this.dapperContext.GetTransaction() == null)
                        {
                            this.dapperContext.BeginTransaction();
                        }
                    }
                }
            }
            if (dapperHelper == null)
            {
                dapperHelper = new DapperHelper(dapperContext);
            }
            return dapperHelper;
        }

        /// <summary>
        /// 开始数据事务
        /// </summary>
        public void BeginTransaction()
        {
            this.HasTransaction = true;
        }

        /// <summary>
        /// 提交工作单元
        /// </summary>
        /// <returns>受影响行数</returns>
        public int Commit()
        {
            int result = 0;
            try
            {
                if (this.EFContextIsExist && this.efContext.ChangeTracker.HasChanges())
                {
                    result += this.efContext.SaveChanges();
                    if ((!this.DapperContextIsExist) && this.HasTransaction)
                        this.efContext.Database.CurrentTransaction.Commit();
                }
                if (this.DapperContextIsExist && this.HasTransaction)
                {
                    result += this.dapperContext.Commit();
                }
            }
            catch(Exception ex)
            {
                Rollback();
                throw ex;
            }
            this.HasTransaction = false;
            return result;
        }

        /// <summary>
        /// 执行回滚事务
        /// </summary>
        public void Rollback()
        {
            if (this.EFContextIsExist && this.efContext.ChangeTracker.HasChanges())
            {
                var entities = this.efContext.ChangeTracker.Entries();
                foreach (var entity in entities.Where(e => e.State == System.Data.Entity.EntityState.Added || e.State == System.Data.Entity.EntityState.Modified || e.State == System.Data.Entity.EntityState.Deleted))
                {
                    entity.State = System.Data.Entity.EntityState.Detached;
                }
            }
            if (this.DapperContextIsExist && this.HasTransaction)
            {
                this.dapperContext.Rollback();
            }
            this.HasTransaction = false;
        }

        #region 释放资源

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true); GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否释放</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.EFContextIsExist)
                    this.efContext.Dispose();
                if (this.DapperContextIsExist)
                    this.dapperContext.Dispose();
            }
        }

        #endregion
    }
}
